#include "leam-qt-stream.hh"
#include "leam-errors.hh"
#include <QAbstractSocket>
#include <iostream>

using namespace std;

namespace leam 
{

    bool LeamQAbstractSocketInStream::hasData()
    {
	cout << "Checking bytes available" << endl;
	return socket != NULL && socket->bytesAvailable() > 0;
    }
#define CHECK_READ(val) readBytes( (char*)(&val), sizeof( val ))

#define CHECK_STREAM_READ_VALUE(val) if (socket) { CHECK_READ( val ); } else val = 0;
    void LeamQAbstractSocketInStream::operator>>( licada::uint8& val )
    {
	CHECK_STREAM_READ_VALUE( val );
    }
    void LeamQAbstractSocketInStream::operator>>( licada::uint16& val )
    {
	CHECK_STREAM_READ_VALUE( val );
    }
    //message id, length
    void LeamQAbstractSocketInStream::operator>>( licada::uint32& val )
    {
	CHECK_STREAM_READ_VALUE( val );
    }
    //Write this as if it was a data vector, and don't write the null
    //character at the end.
    void LeamQAbstractSocketInStream::operator>> ( std::string& val )
    {
	if ( socket == NULL || val.length() == 0 ) return;
	readBytes( &val[0], val.length() );
    }
    //pre-allocated, read up to data.size bytes
    void LeamQAbstractSocketInStream::operator>>( licada::TDataVector& data )
    {
	if ( socket == NULL || data.size() == 0 )
	    return;
	readBytes( (char*)&(*data.begin()), data.size());
    }
    void LeamQAbstractSocketInStream::ignore( licada::uint32 numBytes )
    {
	if ( socket == NULL || numBytes == 0 )
	    return;
	ignoreBuffer.resize( numBytes );
	(*this) >> ignoreBuffer;
    }
    void LeamQAbstractSocketInStream::close()
    {
	if ( socket )
	    socket->close();
	socket = NULL;
    }

    void LeamQAbstractSocketInStream::readBytes( char* buffer, licada::uint32 length )
    {
	if ( socket == NULL )
	    return;
	cout << "Bytes available: " << socket->bytesAvailable() << " length: " << length << endl;
	while (socket->bytesAvailable() < length) {
            if (!socket->waitForReadyRead(5000)) {
                throw LeamReadError();
                return;
            }
        }
	qint64 len = socket->read( buffer, length);
	if ( len != (qint64)length ) {
	    throw LeamReadError();
	}
    }

#define WRITE_VALUE( val ) \
if ( socket == NULL ) return; \
if ( socket->write((char*)(&val), sizeof( val)) != sizeof(val)) \
    throw LeamWriteError();

    void LeamQAbstractSocketOutStream::operator<<( licada::uint8 val )
    {
	WRITE_VALUE( val );
    }
    void LeamQAbstractSocketOutStream::operator<<( licada::uint16 val )
    {
	WRITE_VALUE( val );
    }
    void LeamQAbstractSocketOutStream::operator<<( licada::uint32 val )
    {
	WRITE_VALUE( val );
    }
    void LeamQAbstractSocketOutStream::operator<<( const std::string& val )
    {
	if ( socket == NULL || val.length() == 0 )
	    return;
	qint64 written = socket->write( val.c_str(), val.length() );
	if ( written != val.length() )
	    throw LeamWriteError();
    }
    void LeamQAbstractSocketOutStream::operator<<( const licada::TDataVector& data )
    {
	if ( socket == NULL || data.size() == 0 )
	    return;
	qint64 written = socket->write( (const char*)&(*data.begin()), data.size());
	if ( written != data.size() )
	    throw LeamWriteError();
    }
    void LeamQAbstractSocketOutStream::flush() 
    {
	if ( socket != NULL ) socket->flush();
	cout << "flushing" << endl;
    }
    void LeamQAbstractSocketOutStream::close()
    {
	if ( socket != NULL )
	    socket->close();
	socket = NULL;
    }
}
